package com.dc.dbus.transport.netty.client.node;

import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

import com.dc.dbus.transport.netty.Log;
import com.dc.dbus.transport.netty.client.NettyClient;

public class NodeLink extends NodeInfo {
	
	final AtomicInteger index = new AtomicInteger(0);
	
	public volatile NettyClient[] clients;
	
	/**
	 * 每次调用都会记录该次调用的回馈的最大时间（多个调用中的最大值），
	 * 用于关闭node时候，当invokeSessionExpireTime<System.currentTimeMillis()时，该node下所有channel将被关闭
	 */
	AtomicLong invokeSessionExpireTime = new AtomicLong(System.currentTimeMillis() + 60000);
	
	public NodeLink(String serviceName, String nodeName, String[] routers, String[] hosts, int[] ports, NettyClient[] clients) {
		super(serviceName, nodeName, routers, hosts, ports);
		this.clients = clients;
	}
	
	
	
	@Override
	public String toString() {
		return "NodeLink [serviceName=" + serviceName
				+ ", nodeName=" + nodeName
				+ ", nowChannelCount:" + clients.length
				+ ", ips:" + Arrays.toString(hosts)
				+ ", ports:" + Arrays.toString(ports)
				+ ", routers:" + Arrays.toString(routers)
				+ "]";
	}

	public long getInvokeSessionExpireTime() {
		return invokeSessionExpireTime.get();
	}

	public void close() {
		NettyClient[] clients = this.clients;
		for (NettyClient c : clients) {
			try {
				c.close();
			} catch (Exception e) {
				Log.nettyLinkLogger.error("NodeLink.close Exception", e);
			}
		}
	}

	public boolean writeAndFlush(Object obj, int invokeSessionTimeout) {
		
		NettyClient[] clients = this.clients;
		if (clients.length == 0) {
			return false;
		}

		int next = index.getAndIncrement() % clients.length;
		NettyClient c = clients[next];

		// 测试代码
//		c = clients[0];
		
		c.getChannel().writeAndFlush(obj);
		
		if (Log.nettyLinkLogger.isDebugEnabled()) {
			Log.nettyLinkLogger.debug("NodeLink.writeAndFlush to channel:" + c.getHost() + c.getPort() + ", obj:" + obj);
		}
		
		// =0 表示没有任何回馈，所以不需要更新失效时间
		if (invokeSessionTimeout > 0) {
			long sessionExpireTime = System.currentTimeMillis() + invokeSessionTimeout;
			for (;;) {
				long current = invokeSessionExpireTime.get();
	            if (sessionExpireTime > current) {
	            	if (invokeSessionExpireTime.compareAndSet(current, sessionExpireTime)) {
		            	break;
		            }
	            } else {
	            	break;
	            }
	        }
		}
		
		return true;
	}
}